/**
 * @file ChaCha20_Crypto.c
 * @brief ChaCha20加密算法实现及令牌处理功能
 * @details 该文件实现了ChaCha20流密码算法，以及基于该算法的令牌生成、解密和验证功能
 */

#include <stdint.h>
#include <string.h>
#include "PLT_HEADS.h"  // 包含M0平台特定的头文件
#include "YLO_SIC518_CMD_UPTE_LK.H"
#include <time.h>

/**
 * @def RAND_A
 * @brief 线性同余法随机数生成器乘数
 */
#define RAND_A 1664525

/**
 * @def RAND_C
 * @brief 线性同余法随机数生成器增量
 */
#define RAND_C 1013904223

/**
 * @def RAND_M
 * @brief 线性同余法随机数生成器模数
 */
#define RAND_M 0xFFFFFFFF

// 全局变量定义
uint8_t locknum[4] = {0}; ///< 锁码存储缓冲区

/**
 * @brief ChaCha20常量 ("expand 32-byte k")
 */
const uint32_t CONSTANTS[4] = {0x61707865, 0x3320646e, 0x79622d32, 0x6b206574};

/**
 * @brief 32位左旋转函数
 * @param[in] x 待旋转的32位数值
 * @param[in] n 旋转位数
 * @return 旋转后的32位数值
 */
static inline uint32_t rotl32(uint32_t x, int n) {
    return (x << n) | (x >> (32 - n));
}

/**
 * @brief ChaCha20四分之一轮操作
 * @param[in,out] a 第一个状态字
 * @param[in,out] b 第二个状态字
 * @param[in,out] c 第三个状态字
 * @param[in,out] d 第四个状态字
 */
static inline void chacha20_quarter_round(uint32_t *a, uint32_t *b, uint32_t *c, uint32_t *d) {
    *a = (*a + *b);
    *d ^= *a;
    *d = rotl32(*d, 16);
    
    *c = (*c + *d);
    *b ^= *c;
    *b = rotl32(*b, 12);
    
    *a = (*a + *b);
    *d ^= *a;
    *d = rotl32(*d, 8);
    
    *c = (*c + *d);
    *b ^= *c;
    *b = rotl32(*b, 7);
}

/**
 * @brief 生成64字节的ChaCha20密钥流块
 * @param[in] key 32字节密钥
 * @param[in] counter 32位计数器
 * @param[in] nonce 12字节随机数
 * @param[out] out 输出的64字节密钥流
 */
void chacha20_block(const uint8_t *key, uint32_t counter, const uint8_t *nonce, uint8_t *out) {
    uint32_t state[16];
    uint32_t working_state[16];
    
    // 初始化状态矩阵
    state[0] = CONSTANTS[0];
    state[1] = CONSTANTS[1];
    state[2] = CONSTANTS[2];
    state[3] = CONSTANTS[3];
    
    // 小端序密钥
    for (int i = 0; i < 8; i++) {
        state[4+i] = ((uint32_t)key[i*4+3] << 24) | 
                     ((uint32_t)key[i*4+2] << 16) | 
                     ((uint32_t)key[i*4+1] << 8)  | 
                     (uint32_t)key[i*4];
    }
    
    state[12] = counter;
    for (int i = 0; i < 3; i++) {
        state[13+i] = ((uint32_t)nonce[i*4+3] << 24) | 
                      ((uint32_t)nonce[i*4+2] << 16) | 
                      ((uint32_t)nonce[i*4+1] << 8)  | 
                      (uint32_t)nonce[i*4];
    }
    
    // 复制初始状态
    for(int i = 0; i < 16; i++) 
        working_state[i] = state[i];
    
    // 执行10轮操作
    for (int round = 0; round < 10; round++) {
        // 列轮
        chacha20_quarter_round(&working_state[0], &working_state[4], &working_state[8], &working_state[12]);
        chacha20_quarter_round(&working_state[1], &working_state[5], &working_state[9], &working_state[13]);
        chacha20_quarter_round(&working_state[2], &working_state[6], &working_state[10], &working_state[14]);
        chacha20_quarter_round(&working_state[3], &working_state[7], &working_state[11], &working_state[15]);
        
        // 对角轮
        chacha20_quarter_round(&working_state[0], &working_state[5], &working_state[10], &working_state[15]);
        chacha20_quarter_round(&working_state[1], &working_state[6], &working_state[11], &working_state[12]);
        chacha20_quarter_round(&working_state[2], &working_state[7], &working_state[8], &working_state[13]);
        chacha20_quarter_round(&working_state[3], &working_state[4], &working_state[9], &working_state[14]);
    }
    
    // 添加初始状态到工作状态
    for (int i = 0; i < 16; i++) {
        working_state[i] += state[i];
    }
    
    // 输出密钥流（小端序）
    for (int i = 0; i < 16; i++) {
        *out++ = (uint8_t)(working_state[i]);
        *out++ = (uint8_t)(working_state[i] >> 8);
        *out++ = (uint8_t)(working_state[i] >> 16);
        *out++ = (uint8_t)(working_state[i] >> 24);
    }
}

/**
 * @brief ChaCha20加密/解密函数
 * @param[in] key 32字节密钥
 * @param[in] counter 32位计数器
 * @param[in] nonce 12字节随机数
 * @param[in] plaintext 输入明文/密文数据
 * @param[out] encrypted 输出密文/明文数据
 * @param[in] len 数据长度
 * @note 加密和解密使用相同的函数，因为ChaCha20是对称流密码
 */
void chacha20_encrypt(const uint8_t *key, uint32_t counter, const uint8_t *nonce, 
                      const uint8_t *plaintext, uint8_t *encrypted, size_t len) {
    uint8_t key_stream[64];
    
    for (size_t i = 0; i < len; i += 64) {
        chacha20_block(key, counter + (i >> 6), nonce, key_stream);
        
        size_t block_len = (len - i < 64) ? len - i : 64;
        for (size_t j = 0; j < block_len; j++) {
            encrypted[i + j] = plaintext[i + j] ^ key_stream[j];
        }
    }
}

/**
 * @brief 将十进制数字数组转换为十六进制字节数组
 * @param[in] dec_array 十进制数字数组
 * @param[in] dec_len 十进制数组长度
 * @param[out] hex_array 输出的十六进制字节数组
 * @param[out] hex_bytes 输出的十六进制字节数
 */
void dec_array_to_hex(const uint8_t *dec_array, int dec_len, 
                      uint8_t *hex_array, uint8_t *hex_bytes) {
    if (dec_len < 1 || dec_len > 20) {
        *hex_bytes = 0;
        return;
    }
    
    // 跳过前导零
    int start_index = 0;
    while (start_index < dec_len && dec_array[start_index] == 0) {
        start_index++;
    }
    int eff_len = dec_len - start_index;
    
    if (eff_len == 0) {
        *hex_bytes = 1;
        hex_array[0] = 0;
        return;
    }
    
    // 将十进制数组转换为整数
    uint64_t decimal = 0;
    for (int i = 0; i < eff_len; i++) {
        decimal = decimal * 10 + dec_array[start_index + i];
    }
    
    // 确定输出字节数
    *hex_bytes = (eff_len <= 8) ? 4 : 8;
    
    // 转换为十六进制字节（大端序）
    for (int i = 0; i < *hex_bytes; i++) {
        if (*hex_bytes == 4) {
            uint8_t shifts[4] = {24, 16, 8, 0};
            hex_array[i] = (decimal >> shifts[i]) & 0xFF;
        } else {
            uint8_t shifts[8] = {56, 48, 40, 32, 24, 16, 8, 0};
            hex_array[i] = (decimal >> shifts[i]) & 0xFF;
        }
    }
}

/**
 * @brief 设置加密密钥
 * @note 密钥由MCU ID、MAC ID和客户ID组合而成
 */
void set_key(void) {
    size_t offset = 0;
    memcpy(g_infrequentData.secretKey + offset, g_deviceInfo.mcuId, 12);
    offset += 12;
    
    memcpy(g_infrequentData.secretKey + offset, g_infrequentData.macId, 12);
    offset += 12;
    
    memcpy(g_infrequentData.secretKey + offset, g_infrequentData.customerId, 8);
}

/**
 * @brief 加密4位数字
 * @param[in] plaintext 4位明文数字
 * @param[out] encrypteddata 输出的4字节加密数据
 */
void encrypted_4num(const uint8_t* plaintext, uint8_t* encrypteddata) {
    uint32_t counter = 111;
    uint8_t encrypted[4];
    
    set_key();
    chacha20_encrypt(g_infrequentData.secretKey, counter, g_infrequentData.nonceDefaultKey, plaintext, encrypted, 4);
    memcpy(encrypteddata, encrypted, 4);
}

// 全局随机数种子
unsigned long rand_seed = 1;
volatile uint32_t uninitialized;

/**
 * @brief 初始化随机数种子
 * @param[in] seed 种子值
 */
void srand_m0(unsigned long seed) {
    rand_seed = seed;
}

/**
 * @brief 生成伪随机数
 * @return 范围在1到9999之间的随机数
 */
unsigned int rand_m0() {
    // 线性同余法：X_{n+1} = (a * X_n + c) mod m
    rand_seed = (RAND_A * rand_seed + RAND_C) % RAND_M;
    
    // 映射到1-9999范围
    return (rand_seed % 9999) + 1;
}

/**
 * @brief 从当前时间生成随机种子
 * @return 基于当前时间生成的随机种子
 * @note 时间格式：YYMMDDHHMMSS
 */
unsigned long generate_random_seed_from_currenttime() {
    unsigned long seed = 0;
    
    // 使用图片中完全相同的时间格式
    seed = currentTime.second;                    // 秒
    seed += currentTime.minute * 100;             // 分
    seed += currentTime.hour * 10000;             // 时
    seed += currentTime.day * 1000000;            // 日
    seed += currentTime.month * 100000000;         // 月
    
    // 年份处理 - 取后两位，与图片中的格式一致
    uint8_t year_last_two = currentTime.year % 100;
    seed += (unsigned long)year_last_two * 10000000000;
    
    return seed;
}

/**
 * @brief 生成4位随机数字 (0001-9999)
 * @param[out] output 输出的4位数字字符数组
 */
void generate_4digit_random_m0(uint8_t *output) {
    uint16_t random_num;
    random_num = rand_m0();
    // 将数字转换为4个ASCII字符（逆序存储）
    output[3] = '0' + (random_num / 1000) % 10; // 千位
    output[2] = '0' + (random_num / 100) % 10;  // 百位
    output[1] = '0' + (random_num / 10) % 10;   // 十位
    output[0] = '0' + random_num % 10;          // 个位
}

/**
 * @brief 获取锁码
 * @note 基于当前时间生成随机锁码
 */
void Get_LockNum(void) {  
    srand_m0(generate_random_seed_from_currenttime());   
    generate_4digit_random_m0(locknum);
}

/**
 * @brief 解锁功能
 * @param[in] dec 输入的十进制数字
 * @param[in] plaintext 明文数据
 * @return 0表示解锁成功，非0表示失败
 */
uint8_t UnLock(const uint8_t* dec, const uint8_t* plaintext) {
    uint8_t hex_result[8];
    uint8_t encrypted[4]; 
    uint8_t decrypted[4]; 
    uint8_t bytes = 0;
    
    // 反转明文顺序
    for(uint8_t i = 0;i<4;i++) {
        decrypted[i] = plaintext[3 - i];
    }
    
    dec_array_to_hex(dec, 10, hex_result, &bytes); 
    encrypted_4num(decrypted, encrypted);
    
    // 比较加密结果
    uint8_t j = 0;
    for(uint8_t i = 0; i < 4; i++) {
        if(hex_result[i + 4] != encrypted[i]) {
            j++;
        }
    }
    return j; // 返回0表示解锁成功
}

/**
 * @brief 将十六进制数转换为十进制字符串
 * @param[in] hex_data 输入的十六进制数据
 * @param[in] hex_len 十六进制数据长度
 * @param[out] dec_str 输出的十进制字符串
 * @param[out] dec_len 输出的十进制字符串长度
 */
void hex_to_dec_string(const uint8_t *hex_data, size_t hex_len, char *dec_str, size_t *dec_len) {
    uint64_t value = 0;
    
    // 将十六进制数据转换为64位整数
    for (size_t i = 0; i < hex_len; i++) {
        value = (value << 8) | hex_data[i];
    }
    
    // 将整数转换为十进制字符串
    *dec_len = 0;
    uint64_t temp = value;
    
    // 计算数字位数
    uint64_t divisor = 1;
    while (temp / divisor >= 10) {
        divisor *= 10;
    }
    
    // 转换为字符串
    while (divisor > 0) {
        uint8_t digit = temp / divisor;
        dec_str[(*dec_len)++] = '0' + digit;
        temp %= divisor;
        divisor /= 10;
    }
    
    // 如果值为0
    if (*dec_len == 0) {
        dec_str[(*dec_len)++] = '0';
    }
    
    dec_str[*dec_len] = '\0';
}

/**
 * @brief 构建20位令牌
 * @param[in] hex_data 输入的十六进制数据
 * @param[in] hex_len 十六进制数据长度
 * @param[out] token 输出的20位令牌字符串
 * @note 令牌格式：2位TOKENID + 17位十进制数 + 1位salt
 */
void build_token(const uint8_t *hex_data, size_t hex_len, char *token) {
    // 固定部分 - TOKENID只取前2位（0x31,0x38 → "18"）
    const uint8_t token_id[2] = {0x31, 0x38}; // "18"
    const uint8_t salt = 0x33; // '3'
    
    // 将十六进制转换为十进制字符串
    char dec_str[20] = {0};
    size_t dec_len = 0;
    hex_to_dec_string(hex_data, hex_len, dec_str, &dec_len);
    
    // 补0至17位
    if (dec_len < 17) {
        // 移动现有内容到后面
        memmove(dec_str + (17 - dec_len), dec_str, dec_len);
        // 前面补0
        memset(dec_str, '0', 17 - dec_len);
        dec_len = 17;
    }
    
    // 构建令牌
    int pos = 0;
    
    // 添加TOKENID（只取前2位）
    token[pos++] = token_id[0];
    token[pos++] = token_id[1];
    
    // 添加十进制数
    for (size_t i = 0; i < dec_len; i++) {
        token[pos++] = dec_str[i];
    }
    
    // 添加salt
    token[pos++] = salt;
    
    // 如果不足20位，在前面补0
    if (pos < 20) {
        size_t zeros = 20 - pos;
        // 移动现有内容
        memmove(token + zeros, token, pos);
        // 在前面补0
        memset(token, '0', zeros);
    }
    
    // 确保令牌以空字符结尾
    token[20] = '\0';
}

/**
 * @brief 令牌解密函数
 * @param[in] token 输入的20位令牌
 * @param[in] key 解密密钥
 * @param[in] counter 计数器值
 * @param[in] nonce 随机数
 * @param[out] plaintext 输出的明文数据
 * @param[in] plaintext_len 明文数据长度
 * @return 成功返回0，失败返回-1
 */
int decrypt_token(const char *token, const uint8_t *key, uint32_t counter, 
                  const uint8_t *nonce, uint8_t *plaintext, size_t plaintext_len) {
    // 验证令牌长度
    size_t token_len = 0;
    while (token[token_len] != '\0' && token_len < 21) token_len++;
    if (token_len != 20) {
        return -1;
    }
    
    // 提取中间17位十进制数字
    char dec_str[18] = {0}; // 17位数字 + 空字符
    for (int i = 0; i < 17; i++) {
        dec_str[i] = token[2 + i];
    }
    dec_str[17] = '\0';
    
    // 验证是否为有效数字
    for (int i = 0; i < 17; i++) {
        if (dec_str[i] < '0' || dec_str[i] > '9') {
            return -1;
        }
    }
    
    // 将十进制字符串转换为64位整数
    uint64_t decimal_value = 0;
    for (int i = 0; i < 17; i++) {
        decimal_value = decimal_value * 10 + (dec_str[i] - '0');
    }
    
    // 将整数转换为7字节的十六进制数据（大端序）
    uint8_t encrypted_data[7] = {0};
    for (int i = 0; i < 7; i++) {
        int shift = (6 - i) * 8; // 7字节，所以最高位是左移48位
        encrypted_data[i] = (decimal_value >> shift) & 0xFF;
    }
    
    // 使用ChaCha20解密
    chacha20_encrypt(key, counter, nonce, encrypted_data, plaintext, plaintext_len);
    
    return 0;
}

/**
 * @brief 测试解密指定令牌
 * @param[in] given_token 给定的令牌数据
 * @return 0表示解密成功，1表示失败
 * @note 该函数用于验证令牌的有效性并更新菜单余额
 */
uint8_t test_decrypt_given_token(const uint8_t* given_token) {
    uint8_t returndata = 0;
    uint8_t decrypted_data[7] = {0};
    char token[21] = {0}; // 20个字符 + 1个结束符
    uint32_t counter = 0;
    
    set_key();
    // 计算计数器值
    counter =( (g_infrequentData.initialActiveCount[0] - '0') * 100 )+
             ( (g_infrequentData.initialActiveCount[1] - '0') * 10) +
              (g_infrequentData.initialActiveCount[2] - '0');
    
    // 复制令牌并确保以空字符结尾
    for(uint8_t i = 0; i < 20; i++) {
        token[i] = given_token[i] + '0';
    }
    token[20] = '\0'; // 确保字符串正确终止
    
    // 执行解密
    int result = decrypt_token(token, g_infrequentData.secretKey, counter, 
                               g_frequentData.nonceActiveKey, decrypted_data, 7);
    
    if (result == 0) {
        // 解密成功，处理decrypted_data
        uint8_t tokenid[3] = {0};
        uint8_t tempMenuBalance[4] = {0};    // 临时存储菜单余额
        tokenid[0] = token[0];
        tokenid[1] = token[1];
        tokenid[2] = decrypted_data[0];
        tempMenuBalance[0] = decrypted_data[1];
        tempMenuBalance[1] = decrypted_data[2];
        tempMenuBalance[2] = decrypted_data[3];
        tempMenuBalance[3] = decrypted_data[4];
        
        // 验证令牌ID
        if(memcmp(g_frequentData.tokenId, tokenid, 3) == 0) {
            // 如果当前ID和解密处理的ID相同则处理继续
            uint8_t checksum = 0;
            uint8_t checkdata[9] = {0};
            uint8_t salt = 0;
            
            // 准备校验数据
            checkdata[0] = tokenid[0] - '0';
            checkdata[1] = tokenid[1] - '0';
            checkdata[2] = tokenid[2] - '0';
            checkdata[3] = decrypted_data[1] - '0';
            checkdata[4] = decrypted_data[2] - '0';
            checkdata[5] = decrypted_data[3] - '0';
            checkdata[6] = decrypted_data[4] - '0';
            checkdata[7] = decrypted_data[5] - '0';
            checkdata[8] = decrypted_data[6] - '0';
            
            // 计算校验和和盐值
            for(uint8_t i = 0;i < 9;i++) {
                if(i < 7) {
                    checksum+= checkdata[i];
                }
                if(checkdata[i] == 0) {
                    salt++;
                }
            }
            
            if(salt > 7) {
                returndata = 1;
            } else {
                uint8_t getchecksum = 0;
                getchecksum = checkdata[7]*10 + checkdata[8];
                uint8_t getsalt = 0;
                getsalt = (token[19] - '0');
                
                if(getchecksum == checksum) {
                    if(salt == getsalt) {
                        if((checkdata[0] + checkdata[1] + checkdata[2]) > 10) {
                            // 解密成功准备写入信用
                            LocalUpdateMenuBalance((const uint8_t*) tempMenuBalance);
                        }
                    } else {
                        returndata = 1;
                    }
                } else {
                    returndata = 1;
                }
            }
        } else {
            returndata = 1;
        }
    } else {
        returndata = 1;
    }
    return returndata;
}